/**
 * ReportGear(2011)
 */
package com.reportgear.report.model;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.dom4j.Document;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.reportgear.core.api.XmlSerializer;
import com.reportgear.core.xml.Dom4jUtils;
import com.reportgear.report.model.cell.Cell;
import com.reportgear.report.model.cell.style.Style;
import com.reportgear.report.parameter.model.Parameter;
import com.reportgear.report.setting.ReportSettings;
import com.sot.core.util.NumberUtils;

/**
 * 报表模型读写器
 * 
 * @version 1.0 2011-6-9
 * @author <a herf="lexloo@gmail.com">lexloo</a>
 * @since Report 1.0
 */
public class ReportModelReader {
	protected static final Logger logger = LoggerFactory.getLogger(ReportModelReader.class.getName());

	/**
	 * 模型写入到XML字符串
	 * 
	 * @param model
	 *            模型
	 * @return XML字符串
	 */
	public String writeToXml(ReportModel model) {
		if (logger.isDebugEnabled()) {
			logger.debug("输出模板XML");
		}
		Document doc = Dom4jUtils.createDocument();
		Element root = doc.addElement("report");

		this.writeCommons(root);
		this.writeBaseInfo(root, model);
		this.writeProperties(root, model);
		this.writeParameters(root, model);
		this.writeReportSetting(root, model);

		Map<Style, String> styleList = new HashMap<Style, String>();
		this.writeStyles(root, model, styleList);
		this.writeCells(root, model, styleList);

		return doc.asXML();
	}

	/**
	 * 从XML文档中读入模型信息
	 * 
	 * @param xmlDoc
	 *            文档
	 * @param model
	 *            模型
	 */
	public void readFromXml(String xmlDoc, ReportModel model) {
		if (logger.isDebugEnabled()) {
			logger.debug("读入模板XML");
		}

		Document doc = Dom4jUtils.createDocument(xmlDoc);
		Element root = doc.getRootElement();

		this.readBaseInfo(root, model);
		this.readProperties(root, model);
		this.readReportSetting(root, model);
		this.readParameters(root, model);

		Map<String, Style> styleList = new HashMap<String, Style>();
		this.readStyles(root, model, styleList);
		this.readCells(root, model, styleList);
	}

	/**
	 * 写入通用信息
	 * 
	 * @param root
	 *            根节点
	 */
	private void writeCommons(Element root) {
		// 不需要写入，直接由系统内部固定
	}

	/**
	 * 写入基本信息,子类可扩充自己的信息
	 * 
	 * @param root
	 *            根节点
	 * @param model
	 *            模型
	 */
	protected void writeBaseInfo(Element root, ReportModel model) {
		Element baseEl = root.addElement("base");

		Element el;

		el = baseEl.addElement("id");
		el.addText(String.valueOf(model.getId()));

		el = baseEl.addElement("name");
		el.addText(model.getName());

		el = baseEl.addElement("version");
		el.addText(model.getVersion());

		el = baseEl.addElement("comment");
		el.setText(model.getComment());

		el = baseEl.addElement("class");
		el.setText(model.getClass().getName());
	}

	/**
	 * 写入属性
	 * 
	 * @param root
	 *            根节点
	 * @param model
	 *            模型
	 */
	private void writeProperties(Element root, ReportModel model) {
		Element properties = root.addElement("properties");

		XmlSerializer serializer = model.getColumnPropList();
		serializer.write(properties);

		serializer = model.getRowPropList();
		serializer.write(properties);
	}

	/**
	 * 写入参数
	 * 
	 * @param root
	 *            根节点
	 * @param model
	 *            模型
	 */
	private void writeParameters(Element root, ReportModel model) {
		Element parameters = root.addElement("parameters");
		for (Parameter param : model.getParameters()) {
			Element element = parameters.addElement("parameter");

			param.write(element);
		}
	}

	/**
	 * 写入报表设置
	 * 
	 * @param root
	 *            根节点
	 * @param model
	 *            模型
	 */
	private void writeReportSetting(Element root, ReportModel model) {
		Element el = root.addElement(ReportSettings.XML_TAG);

		model.getReportSettings().write(el);
	}

	/**
	 * 写入样式表， 写入所有单元格样式,不直接从缓存中读，而是取所有单元格中的样式
	 * 
	 * @param root
	 *            根节点
	 * @param model
	 *            模型
	 * @param styleList
	 *            临时样式表
	 */
	private void writeStyles(Element root, ReportModel model, Map<Style, String> styleList) {
		Element styles = root.addElement("styles");
		int styleCount = 0;
		for (Iterator<Cell> iter = model.getCellIterator(); iter.hasNext();) {
			Cell c = iter.next();
			Style style = c.getStyle();

			if ((style == null) || (styleList.containsKey(style))) {
				continue;
			}

			String id = "style" + ++styleCount;
			styleList.put(style, id);
			Element ele = styles.addElement("style");
			ele.addAttribute("id", id);

			style.write(ele);
		}
	}

	/**
	 * 写入单元格
	 * 
	 * @param root
	 *            根节点
	 * @param model
	 *            模型
	 * @param styleList
	 *            临时样式表
	 */
	private void writeCells(Element root, ReportModel model, Map<Style, String> styleList) {
		Element cells = root.addElement("cells");
		for (Iterator<Cell> iter = model.getCellIterator(); iter.hasNext();) {
			Cell c = iter.next();
			Object value = c.getValue();

			if (value == null && c.getColumnSpan() == 1 && c.getRowSpan() == 1
					&& Style.DEFAULT_STYLE.equals(c.getStyle())) {
				continue;
			}

			Element cell = cells.addElement("cell");
			cell.addAttribute("style", styleList.get(c.getStyle()));
			c.write(cell);
		}
	}

	/**
	 * 读入属性
	 * 
	 * @param root
	 *            根节点
	 */
	protected void readBaseInfo(Element root, ReportModel model) {
		Element el = root.element("base");

		model.setId(NumberUtils.convertToInteger(el.elementTextTrim("id"), 0));
		model.setName(el.elementTextTrim("name"));
		model.setVersion(el.elementTextTrim("version"));
		model.setComment(el.elementTextTrim("comment"));
		// Class 类自己持有
	}

	/**
	 * 读入属性
	 * 
	 * @param root
	 *            根节点
	 */
	private void readProperties(Element root, ReportModel model) {
		Element properties = root.element("properties");
		readLocalProperties(properties, model);
	}

	/**
	 * 读入本地属性
	 * 
	 * @param properties
	 *            属性
	 */
	private void readLocalProperties(Element properties, ReportModel model) {
		if (properties == null) {
			return;
		}

		model.getColumnPropList().read(properties);
		model.getRowPropList().read(properties);
	}

	/**
	 * 读入报表属性
	 * 
	 * @param root
	 *            根节点
	 */
	private void readReportSetting(Element root, ReportModel model) {
		Element el = root.element(ReportSettings.XML_TAG);
		if (el == null) {
			return;
		}

		model.getReportSettings().read(el);
	}

	/**
	 * 读入参数信息
	 * 
	 * @param root
	 *            根节点
	 */
	private void readParameters(Element root, ReportModel model) {
//		logger.info("还没有实现读取参数！");
		// this.parameters.clear();
		// Element parameters = root.element("parameters");
		//
		// if (parameters == null) {
		// return;
		// }
		//
		// for (Object o : parameters.elements("parameter")) {
		// Parameter param = new Parameter();
		// param.read((Element) o);
		//
		// this.addParameter(param);
		// }
	}

	/**
	 * 读入样式表
	 * 
	 * @param root
	 *            根节点
	 * @param styleList
	 *            临时样式表
	 */
	private void readStyles(Element root, ReportModel model, Map<String, Style> styleList) {
		Element styles = root.element("styles");
		int styleCount = 0;

		if (styles == null) {
			return;
		}

		for (Object o : styles.elements("style")) {
			Element el = (Element) o;
			Style style = Style.DEFAULT_STYLE;

			style = style.read(el);
			String id = "style" + ++styleCount;
			styleList.put(id, style);
		}
	}

	/**
	 * 读入单元格
	 * 
	 * @param root
	 *            根节点
	 * @param styleList
	 *            临时样式表
	 */
	private void readCells(Element root, ReportModel model, Map<String, Style> styleList) {
		Element cells = root.element("cells");
		if (cells == null) {
			return;
		}

		for (Object o : cells.elements("cell")) {
			Element el = (Element) o;
			int colSpan = NumberUtils.convertToInteger(el.attributeValue("cs"));
			int rowSpan = NumberUtils.convertToInteger(el.attributeValue("rs"));

			if ((colSpan < 1) || (rowSpan < 1)) {
				continue;
			}

			int col = NumberUtils.convertToInteger(el.attributeValue("c"));
			int row = NumberUtils.convertToInteger(el.attributeValue("r"));
			Cell cell = model.getCell(col, row);
			String styleId = el.attributeValue("style");
			cell.setStyle(styleList.get(styleId));

			cell.read((Element) o);
			if (cell.getColumnSpan() > 1 || cell.getRowSpan() > 1) {
				for (int i = 0, len1 = cell.getColumnSpan(); i < len1; i++) {
					for (int j = 0, len2 = cell.getRowSpan(); j < len2; j++) {
						Cell c = model.getCell(col + i, row + j);
						if (c != cell) {
							c.setMerged(true);
							c.mergeIntoCell(cell.getColumn(), cell.getRow());
						}
					}
				}
			}
		}
	}
}